<?php
namespace app\extend\WxUtil;

class WxTool
{
    /**
     * 获取session_key
     * @param string $code
     * @return array|mixed
     */
    public function jscode2session($appid, $secret, $code)
    {
        /**
         * code 换取 session_key
         * 这是一个 HTTPS 接口，开发者服务器使用登录凭证 code 获取 session_key 和 openid。
         * 其中 session_key 是对用户数据进行加密签名的密钥。为了自身应用安全，session_key 不应该在网络上传输。
         */
        $url = 'https://api.weixin.qq.com/sns/jscode2session';
        $result = json_decode($this->httpsGet($url, [
            'appid' => $appid,
            'secret' => $secret,
            'grant_type' => 'authorization_code',
            'js_code' => $code
        ]), true);
        if (isset($result['errcode'])) {
            $this->error = $result['errmsg'];
            return false;
        }
        return $result;
    }

    /**
     * 生成微信小程序二维码
     * @param $appid
     * @param $secret
     * @param $data
     * @return \think\response\Json
     */
    public function wxappQrcode($appid, $secret, $data)
    {
        /**
         *   获取 scene 值方法
         *   scene 字段的值会作为 query 参数传递给小程序/小游戏。用户扫描该码进入小程序/小游戏后，开发者可以获取到二维码中的 scene 值，再做处理逻辑。
         *   调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟，开发工具模拟时的 scene 的参数值需要进行 encodeURIComponent
         *
         *   小程序
         *   Page({
         *     onLoad (query) {
         *       // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
         *       const scene = this.parseScene(decodeURIComponent(query.scene))
         *     },
         *     // 解析 scene 参数
         *     parseScene(scene) {
         *        var params = new Object(); // var params = {};也行
         *        var scene = decodeURIComponent(scene).split('&');
         *        for (var i = 0; i < scene.length; i++) {
         *          params[scene[i].split('=')[0]] = scene[i].split('=')[1];
         *        }
         *        return params;
         *      },
         *   })
         *
         *   小游戏
         *   // 在首次启动时通过 wx.getLaunchOptionsSync 接口获取
         *   const {query} = wx.getLaunchOptionsSync()
         *   const scene = decodeURIComponent(query.scene)
         *
         *   // 或者在 wx.onShow 事件中获取
         *   wx.onShow(function ({query}) {
         *     // scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene
         *     const scene = decodeURIComponent(query.scene)
         *   })
         */
        $access_token = $this->httpsGet('https://api.weixin.qq.com/cgi-bin/token', ['grant_type' => 'client_credential', 'appid' => $appid, 'secret' => $secret]);
        $access_token = json_decode($access_token,true);
        $postData = $this->httpsPost('https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token='.$access_token['access_token'], json_encode([
            // 必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index, 根路径前不要填加 /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
            'page' => $data['page'] ?? 'pages/index/index',
            // 最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）
            'scene' => $data['scene'] ?? '',
            // 	二维码的宽度，单位 px，最小 280px，最大 1280px
            'width' => $data['width'] ?? '430',
            // 正式版为 release，体验版为 trial，开发版为 develop
            'env_version' => 'release',
        ]));
        if(!empty(($postDataDecode = json_decode($postData, true))['errcode'])) {
            if($postDataDecode['errcode'] == 41030) {
                $postDataDecode['errmsg'] = '所传page页面不存在，或者小程序没有发布';
            }elseif($postDataDecode['errcode'] == 45009) {
                $postDataDecode['errmsg'] = '调用分钟频率受限(目前5000次/分钟，会调整)，如需大量小程序码，建议预生成。';
            }elseif($postDataDecode['errcode'] == 40169) {
                $postDataDecode['errmsg'] = 'scene 的长度不合法，最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&\'()*+,/:;=?@-._~，其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）';
            }
            return array('code' => $postDataDecode['errcode'], 'msg' => $postDataDecode['errmsg'], 'data' => '');
        }else{
            return array('code' => 0, 'msg' => '成功', 'data' => base64_encode($postData));
        }
    }

    /**
     * 模拟 GET
     * @param $url
     * @param $data
     * @return bool|string
     */
    public function httpsGet($url, $data = [])
    {
        // 处理query参数
        if (!empty($data)) {
            $url = $url . '?' . http_build_query($data);
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }

    /**
     * 模拟 POST 请求
     * @param $url
     * @param array|string $data
     * @param bool $useCert
     * @param array $sslCert
     * @return mixed
     */
    public function httpsPost($url, $data = [], $useCert = false, $sslCert = [])
    {
        $header = [
            'Content-type: application/json;'
        ];
        if ($useCert == true) {
            file_put_contents($pem1 = sys_get_temp_dir().'/'.time().mt_rand(10000,99999).'.pem', $this->httpsGet($sslCert['certPem']));
            file_put_contents($pem2 = sys_get_temp_dir().'/'.time().mt_rand(10000,99999).'.pem', $this->httpsGet($sslCert['keyPem']));
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POST, TRUE);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        if ($useCert == true) {
            // 设置证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLCERT, $pem1);
            curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($curl, CURLOPT_SSLKEY, $pem2);
        }
        $result = curl_exec($curl);
        curl_close($curl);
        if ($useCert == true) {
            unlink($pem1); unlink($pem2);
        }
        return $result;
    }

    public function getTmpPathByContent($content)
    {
        static $tmpFile = null;
        $tmpFile = tmpfile();
        fwrite($tmpFile, $content);
        $tempPemPath = stream_get_meta_data($tmpFile);
        return $tempPemPath['uri'];
    }
}
